Micron Document
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
| SparkN0de-git | SparkN0de |
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Node / ReticulumProjects / MeshChatX.git / files / meshchatx / src / backend / interface_module_store.py

Displaying Raw • Download


meshchatx/src/backend/interface_module_store.py fa46dc4ce9ab50cbe66a7a9e4b3bca0140102345 (fa46dc4c) Text, 7.70 KB

T8b949e# SPDX-License-Identifier: 0BSD

T8b949e"""Install custom Reticulum interface modules into configdir/interfaces."""

Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations

Tff7b72import T7ee787contextlib
Tff7b72import T7ee787os
Tff7b72import T7ee787re
Tff7b72import T7ee787tempfile

Te6edf3_MODULE_NAME_RE Tff7b72= Te6edf3reTff7b72.Td2a8ffcompileTb4b4b4(Ta5d6ffrTa5d6ff"Ta5d6ff^[A-Za-z_][A-Za-z0-9_]*$Ta5d6ff"Tb4b4b4)
Te6edf3_MAX_MODULE_BYTES Tff7b72= T79c0ff512 Tff7b72* T79c0ff1024
Te6edf3_BUNDLED_INTERFACE_MODULES Tff7b72= Tb4b4b4(Ta5d6ff"Ta5d6ffHTTPInterface.pyTa5d6ff"Tb4b4b4,Tb4b4b4)


Tff7b72def Td2a8ffinterface_modules_dirTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Return the RNS interfacepath directory for this MeshChatX instance."""
Tff7b72if Tff7b72not Te6edf3reticulum_config_dirTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(Ta5d6ff"Ta5d6ffReticulum config directory is not configuredTa5d6ff"Tb4b4b4)
Te6edf3root Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexpanduserTb4b4b4(Tffa657strTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)Tb4b4b4)Tb4b4b4)
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3rootTb4b4b4, Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4)


Tff7b72def Td2a8ffbundled_interface_modules_dirTb4b4b4(Tb4b4b4) Tff7b72-Tff7b72> Tffa657strTb4b4b4:
T8b949e"""Return the package-data directory of MeshChatX-shipped interface modules."""
Tff7b72return Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Tff7b72__file__Tb4b4b4)Tb4b4b4, Ta5d6ff"Ta5d6ffdataTa5d6ff"Tb4b4b4, Ta5d6ff"Ta5d6ffinterfacesTa5d6ff"Tb4b4b4)


Tff7b72def Td2a8ffresolve_bundled_interface_module_pathTb4b4b4(Te6edf3filenameTb4b4b4: Tffa657strTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Return an absolute path to a bundled TypeName.py, or None when missing."""
Te6edf3stem_name Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Tffa657strTb4b4b4(Te6edf3filename Tff7b72or Ta5d6ff"Ta5d6ff"Tb4b4b4)Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3stem_nameTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.pyTa5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3packaged Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3bundled_interface_modules_dirTb4b4b4(Tb4b4b4)Tb4b4b4, Te6edf3stem_nameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3packagedTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3packaged
T8b949e# Dev checkout: vendor/rns_over_http next to the repo root.
Te6edf3repo_vendor Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffabspathTb4b4b4(
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(
Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffdirnameTb4b4b4(Tff7b72__file__Tb4b4b4)Tb4b4b4,
Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ff..Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffvendorTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrns_over_httpTa5d6ff"Tb4b4b4,
Te6edf3stem_nameTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3repo_vendorTb4b4b4)Tb4b4b4:
Tff7b72return Te6edf3repo_vendor
Tff7b72return Tff7b72None


Tff7b72def Td2a8ffensure_bundled_interface_modulesTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657listTb4b4b4[Tffa657dictTb4b4b4]Tb4b4b4:
T8b949e"""Install or refresh MeshChatX-bundled interface modules into interfacepath.

Returns a list of install result dicts for modules that were written.
Missing bundled sources are skipped. Identical files are left untouched.
"""
Te6edf3writtenTb4b4b4: Tffa657listTb4b4b4[Tffa657dictTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Tff7b72not Te6edf3reticulum_config_dirTb4b4b4:
Tff7b72return Te6edf3written
Tff7b72for Te6edf3filename Tff7b72in Te6edf3_BUNDLED_INTERFACE_MODULESTb4b4b4:
Te6edf3source_path Tff7b72= Te6edf3resolve_bundled_interface_module_pathTb4b4b4(Te6edf3filenameTb4b4b4)
Tff7b72if Te6edf3source_path Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72continue
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3source_pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Te6edf3data Tff7b72= Te6edf3handleTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Tff7b72continue
Te6edf3stem Tff7b72= Te6edf3sanitize_interface_module_stemTb4b4b4(Te6edf3filenameTb4b4b4)
Tff7b72if Te6edf3stem Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72continue
Te6edf3target_dir Tff7b72= Te6edf3interface_modules_dirTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)
Te6edf3target_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3target_dirTb4b4b4, Ta5d6fffTa5d6ff"Tffd700{Te6edf3stemTffd700}Ta5d6ff.pyTa5d6ff"Tb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3target_pathTb4b4b4)Tb4b4b4:
Tff7b72tryTb4b4b4:
Tff7b72with Tffa657openTb4b4b4(Te6edf3target_pathTb4b4b4, Ta5d6ff"Ta5d6ffrbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Te6edf3existing Tff7b72= Te6edf3handleTff7b72.Td2a8ffreadTb4b4b4(Tb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Te6edf3existing Tff7b72= Tff7b72None
Tff7b72if Te6edf3existing Tff7b72== Te6edf3dataTb4b4b4:
Tff7b72continue
Tff7b72tryTb4b4b4:
Te6edf3writtenTff7b72.Td2a8ffappendTb4b4b4(
Te6edf3install_interface_moduleTb4b4b4(
Te6edf3reticulum_config_dirTb4b4b4,
Te6edf3filenameTff7b72=Te6edf3filenameTb4b4b4,
Te6edf3dataTff7b72=Te6edf3dataTb4b4b4,
Te6edf3overwriteTff7b72=Tff7b72TrueTb4b4b4,
Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72except Tf85149ValueErrorTb4b4b4:
Tff7b72continue
Tff7b72return Te6edf3written


Tff7b72def Td2a8ffsanitize_interface_module_stemTb4b4b4(Te6edf3nameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Return a safe TypeName stem, or None when the name is invalid."""
Tff7b72if Tff7b72not Te6edf3name Tff7b72or Tff7b72not Tffa657isinstanceTb4b4b4(Te6edf3nameTb4b4b4, Tffa657strTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Te6edf3raw Tff7b72= Te6edf3nameTff7b72.Td2a8ffstripTb4b4b4(Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3raw Tff7b72or Ta5d6ff"Ta5d6ff/Ta5d6ff" Tff7b72in Te6edf3raw Tff7b72or Ta5d6ff"Tffea00\\Ta5d6ff" Tff7b72in Te6edf3raw Tff7b72or Ta5d6ff"Ta5d6ff..Ta5d6ff" Tff7b72in Te6edf3rawTb4b4b4:
Tff7b72return Tff7b72None
Te6edf3stem Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffbasenameTb4b4b4(Te6edf3rawTb4b4b4)
Tff7b72if Te6edf3stemTff7b72.Td2a8fflowerTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.pyTa5d6ff"Tb4b4b4)Tb4b4b4:
Te6edf3stem Tff7b72= Te6edf3stemTb4b4b4[Tb4b4b4:Tff7b72-T79c0ff3Tb4b4b4]
Tff7b72if Tff7b72not Te6edf3_MODULE_NAME_RETff7b72.Td2a8fffullmatchTb4b4b4(Te6edf3stemTb4b4b4)Tb4b4b4:
Tff7b72return Tff7b72None
Tff7b72return Te6edf3stem


Tff7b72def Td2a8ffvalidate_interface_module_sourceTb4b4b4(Te6edf3dataTb4b4b4: Tffa657bytesTb4b4b4) Tff7b72-Tff7b72> Tffa657str Tff7b72| Tff7b72NoneTb4b4b4:
T8b949e"""Return an error message when module bytes are unsafe or incomplete."""
Tff7b72if Tff7b72not Te6edf3dataTb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffInterface module file is emptyTa5d6ff"
Tff7b72if Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4) Tff7b72> Te6edf3_MAX_MODULE_BYTESTb4b4b4:
Tff7b72return Ta5d6fffTa5d6ff"Ta5d6ffInterface module is too large (max Tffd700{Te6edf3_MAX_MODULE_BYTESTffd700}Ta5d6ff bytes)Ta5d6ff"
Tff7b72tryTb4b4b4:
Te6edf3text Tff7b72= Te6edf3dataTff7b72.Td2a8ffdecodeTb4b4b4(Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Tff7b72except Tf85149UnicodeDecodeErrorTb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffInterface module must be UTF-8 textTa5d6ff"
Tff7b72if Ta5d6ff"Tffea00\x00Ta5d6ff" Tff7b72in Te6edf3textTb4b4b4:
Tff7b72return Ta5d6ff"Ta5d6ffInterface module must be plain textTa5d6ff"
Tff7b72if Ta5d6ff"Ta5d6ffinterface_classTa5d6ff" Tff7b72not Tff7b72in Te6edf3textTb4b4b4:
Tff7b72return Tb4b4b4(
Ta5d6ff"Ta5d6ffInterface module must define interface_class Ta5d6ff"
Ta5d6ff"Ta5d6ff(RNS loads TypeName.py and expects that name)Ta5d6ff"
Tb4b4b4)
Tff7b72return Tff7b72None


Tff7b72def Td2a8fflist_interface_modulesTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
T8b949e"""List installed *.py modules under interfacepath."""
Te6edf3path Tff7b72= Te6edf3interface_modules_dirTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)
Te6edf3modulesTb4b4b4: Tffa657listTb4b4b4[Tffa657dictTb4b4b4] Tff7b72= Tb4b4b4[Tb4b4b4]
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisdirTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4:
Tff7b72for Te6edf3entry Tff7b72in Tffa657sortedTb4b4b4(Te6edf3osTff7b72.Td2a8fflistdirTb4b4b4(Te6edf3pathTb4b4b4)Tb4b4b4)Tb4b4b4:
Tff7b72if Tff7b72not Te6edf3entryTff7b72.Td2a8ffendswithTb4b4b4(Ta5d6ff"Ta5d6ff.pyTa5d6ff"Tb4b4b4) Tff7b72or Te6edf3entryTff7b72.Td2a8ffstartswithTb4b4b4(Ta5d6ff"Ta5d6ff.Ta5d6ff"Tb4b4b4)Tb4b4b4:
Tff7b72continue
Te6edf3stem Tff7b72= Te6edf3sanitize_interface_module_stemTb4b4b4(Te6edf3entryTb4b4b4)
Tff7b72if Te6edf3stem Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72continue
Te6edf3full Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3pathTb4b4b4, Te6edf3entryTb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3fullTb4b4b4)Tb4b4b4:
Tff7b72continue
Tff7b72tryTb4b4b4:
Te6edf3size Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffgetsizeTb4b4b4(Te6edf3fullTb4b4b4)
Tff7b72except Tf85149OSErrorTb4b4b4:
Te6edf3size Tff7b72= T79c0ff0
Te6edf3modulesTff7b72.Td2a8ffappendTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Te6edf3stemTb4b4b4, Ta5d6ff"Ta5d6fffilenameTa5d6ff"Tb4b4b4: Te6edf3entryTb4b4b4, Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Te6edf3sizeTb4b4b4}Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6ffinterfacepathTa5d6ff"Tb4b4b4: Te6edf3pathTb4b4b4,
Ta5d6ff"Ta5d6ffmodulesTa5d6ff"Tb4b4b4: Te6edf3modulesTb4b4b4,
Tb4b4b4}


Tff7b72def Td2a8ffinstall_interface_moduleTb4b4b4(
Te6edf3reticulum_config_dirTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Tff7b72*Tb4b4b4,
Te6edf3filenameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Te6edf3dataTb4b4b4: Tffa657bytesTb4b4b4,
Te6edf3overwriteTb4b4b4: Tffa657bool Tff7b72= Tff7b72FalseTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
T8b949e"""Write a custom interface module into interfacepath.

Returns a dict with type, filename, path, and interfacepath.
Raises ValueError on validation failures.
"""
Te6edf3err Tff7b72= Te6edf3validate_interface_module_sourceTb4b4b4(Te6edf3dataTb4b4b4)
Tff7b72if Te6edf3errTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(Te6edf3errTb4b4b4)
Te6edf3stem Tff7b72= Te6edf3sanitize_interface_module_stemTb4b4b4(Te6edf3filenameTb4b4b4)
Tff7b72if Te6edf3stem Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6ff"Ta5d6ffFilename must be a Python identifier plus .py (example: WeaveInterface.py)Ta5d6ff"
Tb4b4b4)
Te6edf3target_dir Tff7b72= Te6edf3interface_modules_dirTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)
Te6edf3osTff7b72.Td2a8ffmakedirsTb4b4b4(Te6edf3target_dirTb4b4b4, Te6edf3modeTff7b72=T79c0ff0o700Tb4b4b4, Te6edf3exist_okTff7b72=Tff7b72TrueTb4b4b4)
Te6edf3target_name Tff7b72= Ta5d6fffTa5d6ff"Tffd700{Te6edf3stemTffd700}Ta5d6ff.pyTa5d6ff"
Te6edf3target_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3target_dirTb4b4b4, Te6edf3target_nameTb4b4b4)
Tff7b72if Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffexistsTb4b4b4(Te6edf3target_pathTb4b4b4) Tff7b72and Tff7b72not Te6edf3overwriteTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(
Ta5d6fffTa5d6ff"Tffd700{Te6edf3target_nameTffd700}Ta5d6ff already exists. Re-upload with overwrite enabled Ta5d6ff"
Ta5d6ff"Ta5d6ffto replace it.Ta5d6ff"
Tb4b4b4)
Te6edf3fdTb4b4b4, Te6edf3tmp_path Tff7b72= Te6edf3tempfileTff7b72.Td2a8ffmkstempTb4b4b4(Te6edf3prefixTff7b72=Ta5d6ff"Ta5d6ff.iface_Ta5d6ff"Tb4b4b4, Te6edf3suffixTff7b72=Ta5d6ff"Ta5d6ff.pyTa5d6ff"Tb4b4b4, Tffa657dirTff7b72=Te6edf3target_dirTb4b4b4)
Tff7b72tryTb4b4b4:
Tff7b72with Te6edf3osTff7b72.Td2a8fffdopenTb4b4b4(Te6edf3fdTb4b4b4, Ta5d6ff"Ta5d6ffwbTa5d6ff"Tb4b4b4) Tff7b72as Te6edf3handleTb4b4b4:
Te6edf3handleTff7b72.Td2a8ffwriteTb4b4b4(Te6edf3dataTb4b4b4)
Te6edf3handleTff7b72.Td2a8ffflushTb4b4b4(Tb4b4b4)
Te6edf3osTff7b72.Td2a8fffsyncTb4b4b4(Te6edf3handleTff7b72.Td2a8fffilenoTb4b4b4(Tb4b4b4)Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffreplaceTb4b4b4(Te6edf3tmp_pathTb4b4b4, Te6edf3target_pathTb4b4b4)
Tff7b72except Tf85149ExceptionTb4b4b4:
Tff7b72with Te6edf3contextlibTff7b72.Td2a8ffsuppressTb4b4b4(Tf85149OSErrorTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3tmp_pathTb4b4b4)
Tff7b72raise
Tff7b72with Te6edf3contextlibTff7b72.Td2a8ffsuppressTb4b4b4(Tf85149OSErrorTb4b4b4)Tb4b4b4:
Te6edf3osTff7b72.Td2a8ffchmodTb4b4b4(Te6edf3target_pathTb4b4b4, T79c0ff0o600Tb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Te6edf3stemTb4b4b4,
Ta5d6ff"Ta5d6fffilenameTa5d6ff"Tb4b4b4: Te6edf3target_nameTb4b4b4,
Ta5d6ff"Ta5d6ffpathTa5d6ff"Tb4b4b4: Te6edf3target_pathTb4b4b4,
Ta5d6ff"Ta5d6ffinterfacepathTa5d6ff"Tb4b4b4: Te6edf3target_dirTb4b4b4,
Ta5d6ff"Ta5d6ffsizeTa5d6ff"Tb4b4b4: Tffa657lenTb4b4b4(Te6edf3dataTb4b4b4)Tb4b4b4,
Tb4b4b4}


Tff7b72def Td2a8ffdelete_interface_moduleTb4b4b4(
Te6edf3reticulum_config_dirTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Te6edf3type_nameTb4b4b4: Tffa657str Tff7b72| Tff7b72NoneTb4b4b4,
Tb4b4b4) Tff7b72-Tff7b72> Tffa657dictTb4b4b4:
T8b949e"""Delete an installed interface module by type stem."""
Te6edf3stem Tff7b72= Te6edf3sanitize_interface_module_stemTb4b4b4(Te6edf3type_nameTb4b4b4)
Tff7b72if Te6edf3stem Tff7b72is Tff7b72NoneTb4b4b4:
Tff7b72raise Tf85149ValueErrorTb4b4b4(Ta5d6ff"Ta5d6ffInvalid interface module type nameTa5d6ff"Tb4b4b4)
Te6edf3target_dir Tff7b72= Te6edf3interface_modules_dirTb4b4b4(Te6edf3reticulum_config_dirTb4b4b4)
Te6edf3target_path Tff7b72= Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffjoinTb4b4b4(Te6edf3target_dirTb4b4b4, Ta5d6fffTa5d6ff"Tffd700{Te6edf3stemTffd700}Ta5d6ff.pyTa5d6ff"Tb4b4b4)
Tff7b72if Tff7b72not Te6edf3osTff7b72.Td2a8ffpathTff7b72.Td2a8ffisfileTb4b4b4(Te6edf3target_pathTb4b4b4)Tb4b4b4:
Tff7b72raise Tf85149FileNotFoundErrorTb4b4b4(Ta5d6fffTa5d6ff"Tffd700{Te6edf3stemTffd700}Ta5d6ff.py is not installedTa5d6ff"Tb4b4b4)
Te6edf3osTff7b72.Td2a8ffremoveTb4b4b4(Te6edf3target_pathTb4b4b4)
Tff7b72return Tb4b4b4{
Ta5d6ff"Ta5d6fftypeTa5d6ff"Tb4b4b4: Te6edf3stemTb4b4b4,
Ta5d6ff"Ta5d6fffilenameTa5d6ff"Tb4b4b4: Ta5d6fffTa5d6ff"Tffd700{Te6edf3stemTffd700}Ta5d6ff.pyTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffinterfacepathTa5d6ff"Tb4b4b4: Te6edf3target_dirTb4b4b4,
Tb4b4b4}


──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────